/*
     raw os - Copyright (C)  Lingjun Chen(jorya_txj).

    This file is part of raw os.

    raw os is free software; you can redistribute it it under the terms of the 
    GNU General Public License as published by the Free Software Foundation; 
    either version 3 of the License, or  (at your option) any later version.

    raw os is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
    without even the implied warranty of  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
    See the GNU General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program. if not, write email to jorya.txj@gmail.com
                                      ---

    A special exception to the LGPL can be applied should you wish to distribute
    a combined work that includes raw os, without being obliged to provide
    the source code for any proprietary components. See the file exception.txt
    for full details of how and when the exception can be applied.
*/


/* 	2012-10  Created by jorya_txj
  *	xxxxxx   please added here
  */
  

#include <raw_api.h>


#if (CONFIG_RAW_TASK_0 > 0)

void task_0_tick_handler(RAW_U8 ev, RAW_U8 *event_data)
{
	event_data = event_data;

	#if (CONFIG_RAW_USER_HOOK > 0)
	raw_tick_hook();
	#endif

	/*update system time to calculate whether task timeout happens*/
	tick_list_update();

	/*update task time slice if possible*/
	#if (CONFIG_SCHED_FIFO_RR > 0)
	calculate_time_slice(ev);
	#endif
	
	/*inform the timer task to update software timer*/	
	#if (CONFIG_RAW_TIMER > 0)
	call_timer_task();
	#endif

}

/*
************************************************************************************************************************
*                                       Timer tick function 
*
* Description: This function is called by timer interrupt if CONFIG_RAW_TASK_0 is enabled.
*
* Arguments  :None
*                
*                 
*
*				         
* Returns		 None
*						
* Note(s)    Called by your own system timer interrupt.
*
*             
************************************************************************************************************************
*/
RAW_U16 task_0_tick_post()
{
	
	return raw_task_0_post(&task_0_event_handler, raw_task_active->priority, 0);

}


/*
************************************************************************************************************************
*                                       Post an event to task 0
*
* Description: This function is called to post an event to task 0
*
* Arguments  : p is which event handler         
*                   ---------------------
*			ev is the event signal 
*                   ---------------------
*                   data is the event data
* Returns		
*						
* Note(s)   this function should be called in interrupt to trigger task 0. 
*
*             
************************************************************************************************************************
*/	
RAW_U16 raw_task_0_post(EVENT_HANLDER *p, RAW_U8 ev, RAW_U8 *event_data)
{
	RAW_U32 snum;
	RAW_SR_ALLOC();

	/*this function should not be called in task*/
	if (raw_int_nesting == 0) {

		return RAW_NOT_CALLED_BY_TASK;
	}

	/*fastest way to make task 0 ready*/
	RAW_CPU_DISABLE();

	/*if message is max, probally interrupt is too fast, please check your interrupt*/
	if(nevents_points == MAX_TASK_EVENT) {
		RAW_CPU_ENABLE();
		return RAW_TASK_0_EVENT_EXHAUSTED;
	}


	snum = (fevent_point + nevents_points) & (MAX_TASK_EVENT - 1);
	++nevents_points;

	if (task_0_is_ready == 0) {
		
		task_0_is_ready = 1;
	}


	/*Store the message*/
	task_0_events_queue[snum].ev = ev;
	task_0_events_queue[snum].event_data = event_data;
	task_0_events_queue[snum].p = p;

	/*Update the debug information*/
	if (nevents_points > peak_events) {
		peak_events = nevents_points;
	}

	RAW_CPU_ENABLE();

	return RAW_SUCCESS;
 
}


RAW_INLINE void task_0_switch()
{
	RAW_SR_ALLOC();

	RAW_CPU_DISABLE();

	if (task_0_is_ready) {
		
		RAW_CPU_ENABLE();
		return;
	}
	         
	get_ready_task(&raw_ready_queue);

	CONTEXT_SWITCH(); 

	RAW_CPU_ENABLE();  

}


static void task_0_process(void *pa)
{
	RAW_U8 ev;
	RAW_U8 *data_temp;
	EVENT_HANLDER *receiver;
	RAW_U8 done;
	RAW_S32 i;
	
	RAW_SR_ALLOC();

	pa = pa;
	
	list_delete(&raw_task_0_obj.task_list);
	data_temp = (RAW_U8 *)raw_ready_queue.task_bit_map;
	*data_temp &= ~1;
	
	while (1) {
		
		done = 0;
		
		while (done == 0) {

			/*Get the message info and update it*/
			RAW_CPU_DISABLE();
			
			if (nevents_points) {

				/* There are events that we should deliver. */
				ev = task_0_events_queue[fevent_point].ev;

				data_temp = task_0_events_queue[fevent_point].event_data;
				receiver = task_0_events_queue[fevent_point].p;
			
				fevent_point = (fevent_point + 1) & (MAX_TASK_EVENT - 1);
				--nevents_points;
				RAW_CPU_ENABLE();

				/*exceute the event handler*/
				receiver->handle_event(ev, data_temp);
			}

			else {
				
				task_0_is_ready = 0;

				RAW_CRITICAL_ENTER_CPU_ENABLE();

				/*Find the highest ready task*/
				i = bit_search_first_one(raw_ready_queue.task_bit_map, 1, CONFIG_RAW_PRIO_MAX - 1);

				/*Update the next highest priority task*/
				if (i >= 0) {
					raw_ready_queue.highest_priority = 1 + i;
				} 

				else {

					RAW_ASSERT(0);
				}
				
				RAW_CRITICAL_EXIT_NO_SCHED();

				/*Give to other normal task to run*/
				task_0_switch();
				done = 1;
				
			}
			
		}
		
		
	}
		
}

#if (CONFIG_RAW_ZERO_INTERRUPT > 0)

void int_msg_handler(RAW_U8 ev, RAW_U8 *msg_data)
{
	OBJECT_INT_MSG *int_msg;
	
	RAW_SR_ALLOC();
	
	int_msg = (OBJECT_INT_MSG *)msg_data;
	
	switch (ev) {

		#if (CONFIG_RAW_TASK_SUSPEND > 0)
		
		case RAW_TYPE_SUSPEND:
			task_suspend((RAW_TASK_OBJ *)(int_msg->object));
			break;
			
		#endif

		#if (CONFIG_RAW_TASK_RESUME > 0)
		
		case RAW_TYPE_RESUME:
			task_resume((RAW_TASK_OBJ *)(int_msg->object));
			break;

			
		#endif

		#if (CONFIG_RAW_SEMAPHORE > 0)
		
		case RAW_TYPE_SEM:
			semaphore_put((RAW_SEMAPHORE *)(int_msg->object), WAKE_ONE_SEM);
			break;

		case RAW_TYPE_SEM_ALL:
			semaphore_put((RAW_SEMAPHORE *)(int_msg->object), WAKE_ALL_SEM);
			break;
			
		#endif
		
		
		#if (CONFIG_RAW_QUEUE > 0)
		
		case RAW_TYPE_Q_FRONT:
			msg_post((RAW_QUEUE *)(int_msg->object), int_msg->msg, SEND_TO_FRONT, WAKE_ONE_QUEUE);
			break;

		case RAW_TYPE_Q_END:
			msg_post((RAW_QUEUE *)(int_msg->object), int_msg->msg, SEND_TO_END, WAKE_ONE_QUEUE);
			break;

		case RAW_TYPE_Q_ALL:
			msg_post((RAW_QUEUE *)(int_msg->object), int_msg->msg, int_msg->opt, WAKE_ALL_QUEUE);
			break;

		#endif

		
		#if (CONFIG_RAW_QUEUE_SIZE > 0)
		
		case RAW_TYPE_Q_SIZE_FRONT:
			msg_size_post((RAW_QUEUE_SIZE *)(int_msg->object), int_msg->msg, int_msg->msg_size, SEND_TO_FRONT, WAKE_ONE_QUEUE);
			break;

		case RAW_TYPE_Q_SIZE_END:
			msg_size_post((RAW_QUEUE_SIZE *)(int_msg->object), int_msg->msg, int_msg->msg_size, SEND_TO_END, WAKE_ONE_QUEUE);
			break;

		case RAW_TYPE_Q_SIZE_ALL:
			msg_size_post((RAW_QUEUE_SIZE *)(int_msg->object), int_msg->msg, int_msg->msg_size, int_msg->opt, WAKE_ALL_QUEUE);
			break;

		#endif

		#if (CONFIG_RAW_EVENT > 0)
		
		case RAW_TYPE_EVENT:
			event_set((RAW_EVENT *)(int_msg->object), int_msg->event_flags, int_msg->opt);
			break;
			
		#endif
		
		#if (CONFIG_RAW_IDLE_EVENT > 0)
		
		case RAW_TYPE_IDLE_EVENT_POST:
			event_post((ACTIVE_EVENT_STRUCT *)(int_msg->object), int_msg->msg_size, int_msg->event_flags);
			break;
			
		#endif
		
		default:
			RAW_ASSERT(0);



	}

	RAW_CPU_DISABLE();

	int_msg->next = free_object_int_msg;
	free_object_int_msg = int_msg;
	
	RAW_CPU_ENABLE();

}



void int_msg_init()
{
	OBJECT_INT_MSG *p_msg1;
	OBJECT_INT_MSG *p_msg2;
	RAW_U32 number;

	msg_event_handler.handle_event = int_msg_handler;

	number = OBJECT_INT_MSG_SIZE;

	raw_memset(object_int_msg, 0, sizeof(object_int_msg));
	
	free_object_int_msg = object_int_msg;
	
	/*init the free msg list*/
	p_msg1 = object_int_msg;
	p_msg2 = object_int_msg;
	p_msg2++;

	while (--number) { 

		p_msg1->next = p_msg2;

		p_msg1++;
		p_msg2++;
	}

	/*init  the last free msg*/ 
	p_msg1->next = 0;                      
	
}



RAW_U16 int_msg_post(RAW_U8 type, void *p_obj, void *p_void, RAW_U32 msg_size, RAW_U32 flags, RAW_U8 opt)
{
	RAW_U8 *msg_data;

	RAW_SR_ALLOC();

	RAW_CPU_DISABLE();
	
	msg_data = (RAW_U8 *)free_object_int_msg;

	if (free_object_int_msg == 0) {
		RAW_CPU_ENABLE();
		return RAW_INT_MSG_EXHAUSTED;
	}
	
	free_object_int_msg->type = type;
	free_object_int_msg->object = p_obj;
	free_object_int_msg->msg = p_void;
	free_object_int_msg->msg_size = msg_size;
	free_object_int_msg->event_flags = flags;
	free_object_int_msg->opt = opt;
	
	free_object_int_msg = free_object_int_msg->next;

	RAW_CPU_ENABLE();
	
	return raw_task_0_post(&msg_event_handler, type, msg_data);

}


#endif                  


/*
************************************************************************************************************************
*                                      Init task 0
*
* Description: This function is called to init task 0
*
* Arguments  :NONE   
*
* Returns		
*						
* Note(s)     
*
*             
************************************************************************************************************************
*/	
void raw_task_0_init()
{

	/*Create task 0  to handle fast interrupt event*/
	raw_task_create(&raw_task_0_obj, (RAW_U8  *)"timer_object",  0, 
	0,  0, task_0_stack, TASK_0_STACK_SIZE, task_0_process, 1);

	task_0_event_handler.handle_event = task_0_tick_handler;
	
	#if (CONFIG_RAW_ZERO_INTERRUPT > 0)
	int_msg_init();
	#endif
}

#endif

